﻿using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.DistributedLocking;
using Volo.Abp.Timing;

namespace Acme.Abp.SerialNumberGenerator;

public class InMemoryAutoIncrementedNumberGenerator: IAutoIncrementedNumberGenerator, ISingletonDependency
{
    private readonly ConcurrentDictionary<string, (long, long)> _cache = new();
    private const int DefaultTimeOutSecond = 10;
    private readonly IAbpDistributedLock _distributedLock;
    private readonly IClock _clock;

    public InMemoryAutoIncrementedNumberGenerator(IAbpDistributedLock distributedLock, IClock clock)
    {
        _distributedLock = distributedLock;
        _clock = clock;
    }

    public async ValueTask<long> GetAsync(string identify, long start = 0, long expiredAt = 0)
    {
        await using var locker = await _distributedLock.TryAcquireAsync(identify, TimeSpan.FromSeconds(DefaultTimeOutSecond));
        var r = _cache.AddOrUpdate(identify, (start, expiredAt), (s, l) =>
        {
            if(l.Item2 > 0 && l.Item2 <= new DateTimeOffset(_clock.Now).ToUnixTimeMilliseconds())
            {
                return (start, expiredAt);
            }
            return (l.Item1 += 1L, l.Item2);
        });
        return r.Item1;
    }

    public async ValueTask ResetAsync(string identify, long no, long expiredAt)
    {
        await using var locker = await _distributedLock.TryAcquireAsync(identify, TimeSpan.FromSeconds(DefaultTimeOutSecond));
        _cache.AddOrUpdate(identify, (no, expiredAt), (s, l) => (no, expiredAt));
    }
}